/**
 * Copyright (c) 2012 Nokia Corporation.
 */

package com.nokia.example.imagescaler;

import javax.microedition.lcdui.Image;

/**
 * Image scaler using the pixel mixing algorithm.
 */
public final class PixelMixingScaler extends ImageScaler {
	/**
	 * From ImageScaler.
	 */
	public Image scale(final int newWidth, final int newHeight,
			final Image originalImage) {
		if (newWidth <= 0 || newHeight <= 0 || originalImage == null) {
			throw new IllegalArgumentException(
					"Invalid width or height or the given image is null!");
		}

		final int sourceWidth = originalImage.getWidth();
		final int sourceHeight = originalImage.getHeight();
		final int[] originalRgb = new int[sourceHeight * sourceWidth];
		originalImage.getRGB(originalRgb, 0, sourceWidth, 0, 0, sourceWidth,
				sourceHeight);

		final int[] scaledRgb = new int[newWidth * newHeight];

		final int oWidth = sourceWidth;
		final int[] oX16 = new int[newWidth + 1];

		for (int newX = 0; newX <= newWidth; newX++) {
			oX16[newX] = ((newX * oWidth) << 4) / newWidth;
		}

		final int[] oXStartWidth = new int[newWidth];
		final int[] oXEndWidth = new int[newWidth];

		for (int newX = 0; newX < newWidth; newX++) {
			oXStartWidth[newX] = 16 - (oX16[newX] % 16);
			oXEndWidth[newX] = oX16[newX + 1] % 16;
		}

		final int oHeight = sourceHeight;
		final int[] oY16 = new int[newHeight + 1];

		for (int newY = 0; newY <= newHeight; newY++) {
			oY16[newY] = ((newY * oHeight) << 4) / newHeight;
		}

		for (int newY = 0; newY < newHeight; newY++) {
			final int oY16Start = oY16[newY];
			final int oY16End = oY16[newY + 1];
			final int oYStart = oY16Start >>> 4;
			final int oYEnd = oY16End >>> 4;
			final int oYStartHeight = 16 - (oY16Start % 16);
			final int oYEndHeight = oY16End % 16;

			for (int newX = 0; newX < newWidth; newX++) {
				final int oX16Start = oX16[newX];
				final int oX16End = oX16[newX + 1];
				final int oXStart = oX16Start >>> 4;
				final int oXEnd = oX16End >>> 4;
				int outArea = 0;
				int outColorArea = 0;
				int outAlpha = 0;
				int outRed = 0;
				int outGreen = 0;
				int outBlue = 0;

				for (int j = oYStart; j <= oYEnd; j++) {
					final int areaHeight;

					if (oYStart == oYEnd) {
						areaHeight = oY16End - oY16Start;
					} else if (j == oYStart) {
						areaHeight = oYStartHeight;
					} else if (j == oYEnd) {
						areaHeight = oYEndHeight;
					} else {
						areaHeight = 16;
					}

					if (areaHeight == 0) {
						continue;
					}

					for (int i = oXStart; i <= oXEnd; i++) {
						final int areaWidth;

						if (oXStart == oXEnd) {
							areaWidth = oX16End - oX16Start;
						} else if (i == oXStart) {
							areaWidth = oXStartWidth[newX];
						} else if (i == oXEnd) {
							areaWidth = oXEndWidth[newX];
						} else {
							areaWidth = 16;
						}

						if (areaWidth == 0) {
							continue;
						}

						int area = areaWidth * areaHeight;
						outArea += area;
						int argb = originalRgb[i + j * sourceWidth];
						int a = (argb >>> 24);

						if (a == 0) {
							continue;
						}

						area = a * area;
						outColorArea += area;
						final int r = (argb & 0x00ff0000) >>> 16;
						final int g = (argb & 0x0000ff00) >>> 8;
						final int b = argb & 0x000000ff;
						outRed += area * r;
						outGreen += area * g;
						outBlue += area * b;
					}
				}

				if (outColorArea > 0) {
					outAlpha = outColorArea / outArea;
					outRed = outRed / outColorArea;
					outGreen = outGreen / outColorArea;
					outBlue = outBlue / outColorArea;
				}

				scaledRgb[newX + newY * newWidth] = (outAlpha << 24)
						| (outRed << 16) | (outGreen << 8) | outBlue;
			}
		}

		return Image.createRGBImage(scaledRgb, newWidth, newHeight, true);
	}

	/**
	 * From ImageScaler.
	 */
	public String type() {
		return "Pixel mixing";
	}
}
